package adapter

import (
	"bytes"
	"context"
	"fmt"
	"github.com/hyperway/pkg/component"
	"github.com/hyperway/pkg/constant"
	"github.com/labstack/echo/v4"
	"io"
	"net/http"
	"net/url"
	"sync"
)

// EchoContext 实现WebContext接口，适配Echo框架的Context
type EchoContext struct {
	listener  component.WebListener
	echoc     echo.Context
	context   context.Context
	variables map[interface{}]interface{}
	Lock      *sync.RWMutex
}

// NewEchoContext 创建EchoContext实例
func NewEchoContext(listener component.WebListener, ctx echo.Context, requestId string) component.WebContext {
	return &EchoContext{
		echoc:     ctx,
		listener:  listener,
		context:   context.WithValue(ctx.Request().Context(), constant.XRequestID, requestId),
		variables: make(map[interface{}]interface{}, 16),
		Lock:      &sync.RWMutex{},
	}
}

func (w *EchoContext) Context() context.Context {
	return w.context
}

func (w *EchoContext) RequestId() string {
	return w.context.Value(constant.XRequestID).(string)
}

func (w *EchoContext) URI() string {
	return w.Request().RequestURI
}

func (w *EchoContext) URL() *url.URL {
	return w.Request().URL
}

func (w *EchoContext) Method() string {
	return w.Request().Method
}

func (w *EchoContext) Host() string {
	return w.Request().Host
}

func (w *EchoContext) RemoteAddr() string {
	return w.Request().RemoteAddr
}

func (w *EchoContext) Headers() http.Header {
	return w.Request().Header
}

func (w *EchoContext) QueryParams() url.Values {
	return w.echoc.QueryParams()
}

func (w *EchoContext) PathValues() url.Values {
	names := w.echoc.ParamNames()
	pathValues := make(url.Values, len(names))
	for _, n := range names {
		pathValues.Set(n, w.echoc.Param(n))
	}
	return pathValues
}

func (w *EchoContext) FormValues() url.Values {
	f, _ := w.echoc.FormParams()
	return f
}

func (w *EchoContext) PostFormValues() url.Values {
	_, _ = w.echoc.FormParams()
	return w.echoc.Request().PostForm
}

func (w *EchoContext) Cookies() []*http.Cookie {
	return w.echoc.Cookies()
}

func (w *EchoContext) Header(name string) string {
	return w.Request().Header.Get(name)
}

func (w *EchoContext) QueryParam(name string) string {
	return w.echoc.QueryParam(name)
}

func (w *EchoContext) PathValue(name string) string {
	return w.echoc.Param(name)
}

func (w *EchoContext) FormValue(name string) string {
	return w.echoc.FormValue(name)
}

func (w *EchoContext) PostFormValue(name string) string {
	return w.PostFormValues().Get(name)
}

func (w *EchoContext) Cookie(name string) (*http.Cookie, error) {
	return w.echoc.Cookie(name)
}

func (w *EchoContext) Body() (io.ReadCloser, error) {
	return w.Request().Body, nil
}

func (w *EchoContext) SetVariable(key string, value interface{}) {
	// 并发读写控制锁
	w.Lock.Lock()
	defer w.Lock.Unlock()
	w.variables[key] = value
}

func (w *EchoContext) Variable(key string) (interface{}, bool) {
	// 并发读写锁
	w.Lock.RLock()
	defer w.Lock.RUnlock()

	// 本地Variable
	v, ok := w.variables[key]
	if ok {
		return v, true
	}
	// 从Context中加载
	v = w.echoc.Get(key)
	return v, nil != v
}

func (w *EchoContext) Request() *http.Request {
	return w.echoc.Request()
}

func (w *EchoContext) ResponseWriter() http.ResponseWriter {
	return w.echoc.Response()
}

func (w *EchoContext) Write(statusCode int, contentType string, data []byte) error {
	return w.WriteStream(statusCode, contentType, bytes.NewReader(data))
}

func (w *EchoContext) WriteStream(statusCode int, contentType string, reader io.Reader) error {
	writer := w.echoc.Response()
	writer.Header().Set(echo.HeaderContentType, contentType)
	writer.WriteHeader(statusCode)
	if _, err := io.Copy(writer, reader); nil != err {
		return fmt.Errorf("web context write failed, error: %w", err)
	}
	return nil
}

func (w *EchoContext) WriteJSON(statusCode int, data interface{}) error {
	return w.echoc.JSON(statusCode, data)
}

func (w *EchoContext) SetContext(ctx context.Context) context.Context {
	w.context = ctx
	return w.Context()
}
